package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.cache.TsCache;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Resource
    private UserInfoFeignClient userInfoFeignClient;

    @Resource
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Resource
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private TrackInfoFeignClient trackInfoFeignClient;

    @Resource
    private UserAccountFeignClient userAccountFeignClient;

    @Resource
    private KafkaService kafkaService;

    @Resource
    private OrderDerateMapper orderDerateMapper;

    @Resource
    private OrderDetailMapper orderDetailMapper;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        //获取用户是否是vip的信息
        Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
        Assert.notNull(userInfoVoResult, "获取的用户信息结果集为空");
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        Assert.notNull(userInfoVo, "获取的用户信息为空");

        OrderInfoVo orderInfoVo = new OrderInfoVo();

        //  订单原始金额
        BigDecimal originalAmount = new BigDecimal("0.00");
        //  减免总金额
        BigDecimal derateAmount = new BigDecimal("0.00");
        //  订单总价
        BigDecimal orderAmount = new BigDecimal("0.00");
        //  订单明细集合
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //  订单减免明细列表
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        //购买的东西类型由三种，分别是专辑声音和vip所以要先判断订单的类型
        if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(tradeVo.getItemType())) {
            //购买专辑
            //判断用户是否购买过该专辑
            Result<Boolean> isPaidAlbumResult = userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            Assert.notNull(isPaidAlbumResult, "查询购买专辑结果集为空");
            Boolean isPaidAlbum = isPaidAlbumResult.getData();
            Assert.notNull(isPaidAlbum, "查询购买专辑结果为空");
            //判断用户是否购买过专辑，如果有抛出异常
            if (isPaidAlbum) {
                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }
            //获取到专辑信息
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(tradeVo.getItemId());
            Assert.notNull(albumInfoResult, "返回专辑结果集不能为空");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "专辑对象不能为空");
            //获取专辑的价格
            originalAmount = albumInfo.getPrice();
            //判断用户是否是vip以此计算不同的优惠
            if (userInfoVo.getIsVip() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                //判断当前专辑是否对vip打折
                if (albumInfo.getVipDiscount().compareTo(new BigDecimal("-1")) != 0) {
                    //计算折扣金额
                    derateAmount = originalAmount.subtract(originalAmount.multiply(new BigDecimal("10")
                                    .subtract(albumInfo.getVipDiscount()))
                            .divide(new BigDecimal("10")));
                }

            } else {
                if (albumInfo.getDiscount().compareTo(new BigDecimal("-1")) != 0) {
                    //计算折扣金额
                    derateAmount = originalAmount.subtract(originalAmount.multiply(new BigDecimal("10")
                                    .subtract(albumInfo.getDiscount()))
                            .divide(new BigDecimal("10")));
                }
            }
            orderAmount = originalAmount.subtract(derateAmount);
            //记录订单明细
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(albumInfo.getPrice());
            orderDetailVoList.add(orderDetailVo);
            //  添加订单减免
            if (derateAmount.compareTo(BigDecimal.ZERO) != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(originalAmount.subtract(orderAmount));
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(tradeVo.getItemType())) {
            //购买声音
            //判断购买的集数不能小于0
            if (tradeVo.getTrackCount() < 0) {
                throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
            }
            //根据购买集数获取到声音列表
            Result<List<TrackInfo>> trackInfoListResult = trackInfoFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
            Assert.notNull(trackInfoListResult, "声音列表返回结果集为空");
            List<TrackInfo> trackInfoList = trackInfoListResult.getData();
            Assert.notNull(trackInfoList, "声音列表对象为空");
            //得到声音对应的专辑信息
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId());
            Assert.notNull(albumInfoResult, "专辑返回结果集为空");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "专辑对象为空");
            //计算原价
            originalAmount = tradeVo.getTrackCount().intValue() > 0 ?
                    albumInfo.getPrice().multiply(new BigDecimal(tradeVo.getTrackCount()))
                    : albumInfo.getPrice();
            //  计算订单总价
            orderAmount = originalAmount;
            //构建订单结算页的详情列表
            orderDetailVoList = trackInfoList.stream().map(trackInfo -> {
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                orderDetailVo.setItemId(trackInfo.getId());
                orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                orderDetailVo.setItemName(trackInfo.getTrackTitle());
                return orderDetailVo;
            }).toList();
        } else {
            //购买vip
            //  根据id 获取VIP 服务配置信息
            Result<VipServiceConfig> vipServiceConfigResult = vipServiceConfigFeignClient.getVipServiceConfig(tradeVo.getItemId());
            Assert.notNull(vipServiceConfigResult, "返回vip配置结果集不能为空");
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            Assert.notNull(vipServiceConfig, "返回vip配置对象不能为空");
            //获取原始金额
            originalAmount = vipServiceConfig.getPrice();
            //实际金额
            orderAmount = vipServiceConfig.getDiscountPrice();
            //减免金额
            derateAmount = originalAmount.subtract(orderAmount);
            //赋值订单明细
            //记录订单明细
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVoList.add(orderDetailVo);
            //赋值减免明细
            //  添加订单减免
            if (derateAmount.compareTo(BigDecimal.ZERO) != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(originalAmount.subtract(orderAmount));
                orderDerateVoList.add(orderDerateVo);
            }
        }
        //获取交易号
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
        //将流水号放入redis里在提交订单时做重复提交判断
        String key = "tradeNo:" + userId;
        stringRedisTemplate.opsForValue().set(key, tradeNo, 60, TimeUnit.SECONDS);
        //构造结果
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setPayWay("");
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        orderInfoVo.setTimestamp(SignHelper.getTimestamp());
        //获取签名，防止在支付时别用户恶意篡改数据
        String str = JSON.toJSONString(orderInfoVo);
        Map<String, Object> map = JSON.parseObject(str, Map.class);
        orderInfoVo.setSign(SignHelper.getSign(map));
        return orderInfoVo;
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //校验签名是否一致防止数据被串改
        String jsonStr = JSON.toJSONString(orderInfoVo);
        Map<String, Object> map = JSON.parseObject(jsonStr, Map.class);
        map.put("payWay", "");
        SignHelper.checkSign(map);
        //获取交易流水号
        String tradeNo = orderInfoVo.getTradeNo();
        //校验流水号
        String key = "tradeNo:" + userId;
        //保证获取流水号和删除流水号的操作具有原子性，防止在高并发下出现并发问题
        String script = """
                if(redis.call('get',KEYS[1])==ARGV[1]) then
                    return redis.call('del',KEYS[1])
                else
                    return 0
                end
                """;
        Boolean flag = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class),
                List.of(key), tradeNo);
        //表示redis里没有流水号，或者流水号错误
        if (!Boolean.TRUE.equals(flag)) {
            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }
        //声明订单编号
        String orderNo = UUID.randomUUID().toString().replace("-", "");
        //判断支付方式
        if (SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(orderInfoVo.getPayWay())) {
            //微信支付
            this.saveOrder(orderInfoVo, userId, orderNo);
        } else if (SystemConstant.ORDER_PAY_WAY_ALIPAY.equals(orderInfoVo.getPayWay())) {
            //支付宝支付，但是项目前端是微信小程序没有支付宝支付，但是为了程序的可扩展性，留有一个分支
            this.saveOrder(orderInfoVo, userId, orderNo);
        } else {
            //余额支付
            //这里的操作涉及三个微服务模块，且多个请求是存在一个不成功就应该全部回滚的状态，但是本地事务没有办法做到
            //先完成业务，后续引入分布式事务seata框架再完善
            //远程调用：获取到用户可用余额
            //构造远程调用对象
            try {
                AccountDeductVo accountDeductVo = new AccountDeductVo();
                accountDeductVo.setOrderNo(orderNo);
                accountDeductVo.setUserId(userId);
                accountDeductVo.setAmount(orderInfoVo.getOrderAmount());
                //  锁定内容.
                accountDeductVo.setContent(orderInfoVo.getItemType());
                //  service-account: 检查与锁定
                Result result = userAccountFeignClient.checkAndDeduct(accountDeductVo);
                if (200 != result.getCode()) {
                    throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
                }
                //保存订单
                this.saveOrder(orderInfoVo, userId, orderNo);
                ///  更新用户支付记录
                OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
                List<Long> itemIdList = orderInfo.getOrderDetailList().stream().map(OrderDetail::getItemId).collect(Collectors.toList());
                UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
                userPaidRecordVo.setOrderNo(orderNo);
                userPaidRecordVo.setUserId(orderInfo.getUserId());
                userPaidRecordVo.setItemType(orderInfo.getItemType());
                userPaidRecordVo.setItemIdList(itemIdList);
                Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
                //  判断
                if (200 != userResult.getCode()) {
                    throw new GuiguException(211, "新增购买记录异常");
                }
            } catch (GuiguException e) {
                throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
            } catch (Exception e) {
                kafkaService.sendMessage(orderNo, KafkaConstant.QUEUE_ACCOUNT_UNLOCK);
                e.printStackTrace();
            }
        }
        return orderNo;
    }

    @Override
    public void orderCancel(Long orderId) {
        OrderInfo orderInfo = this.getById(orderId);
        //  判断
        if (null != orderInfo && orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_UNPAID)) {
            //  取消订单
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            this.updateById(orderInfo);
        }
    }

    @Override
    public IPage<OrderInfo> findUserPage(Page<OrderInfo> pageParam, Long userId) {
        IPage<OrderInfo> infoIPage = this.baseMapper.selectUserPage(pageParam, userId);
        infoIPage.getRecords().forEach(item -> {
            //  设置状态名
            item.setOrderStatusName(getOrderStatusName(item.getOrderStatus()));
            item.setPayWayName(getPayWayName(item.getPayWay()));
        });
        return infoIPage;
    }


    /**
     * 根据订单状态获取到订单名称
     *
     * @param orderStatus
     * @return
     */
    private String getOrderStatusName(String orderStatus) {
        //  声明订单状态名称
        String orderStatusName = "";
        //  判断
        if (SystemConstant.ORDER_STATUS_UNPAID.equals(orderStatus)) {
            orderStatusName = "未支付";
        } else if (SystemConstant.ORDER_STATUS_PAID.equals(orderStatus)) {
            orderStatusName = "已支付";
        } else {
            orderStatusName = "已取消";
        }
        //  返回
        return orderStatusName;
    }

    /**
     * 保存订单数据方法
     *
     * @param orderInfoVo
     * @param userId
     * @param orderNo
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrder(OrderInfoVo orderInfoVo, Long userId, String orderNo) {
        //向 order_info order_detail order_derate三张表里添加数据
        //向order_info保存数据
        OrderInfo orderInfo = new OrderInfo();
        //  属性拷贝
        BeanUtils.copyProperties(orderInfoVo, orderInfo);
        orderInfo.setOrderNo(orderNo);
        String orderTitle = orderInfoVo.getOrderDetailVoList().get(0).getItemName();
        orderInfo.setOrderTitle(orderTitle);
        orderInfo.setUserId(userId);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        this.save(orderInfo);
        //向order_detail保存数据
        // 订单明细
        if (!CollectionUtils.isEmpty(orderInfoVo.getOrderDetailVoList())) {
            orderInfoVo.getOrderDetailVoList().forEach(orderDetailVo -> {
                OrderDetail orderDetail = new OrderDetail();
                BeanUtils.copyProperties(orderDetailVo, orderDetail);
                orderDetail.setOrderId(orderInfo.getId());
                orderDetailMapper.insert(orderDetail);
            });
        }

        //向order_derate保存数据
        // 订单减免
        if (!CollectionUtils.isEmpty(orderInfoVo.getOrderDerateVoList())) {
            orderInfoVo.getOrderDerateVoList().forEach(orderDerateVo -> {
                OrderDerate orderDerate = new OrderDerate();
                BeanUtils.copyProperties(orderDerateVo, orderDerate);
                orderDerate.setOrderId(orderInfo.getId());
                orderDerateMapper.insert(orderDerate);
            });
        }
        //判断支付方式
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())) {
            //保存用户的购买记录
            this.orderPaySuccess(orderNo);
        } else {
            //如果是在线支付，就发送一个延迟消息，在一定时间内用户没有支付就自动取消订单
            this.sendDelayMessage(orderInfo.getId());
        }
    }

    /**
     * 发送延迟消息取消订单
     *
     * @param id
     */
    private void sendDelayMessage(Long id) {
        try {
            //获取阻塞队列
            RBlockingDeque<String> blockingDeque = this.redissonClient.getBlockingDeque(KafkaConstant.QUEUE_ORDER_CANCEL);
            //获取延迟队列
            RDelayedQueue<String> delayedQueue = this.redissonClient.getDelayedQueue(blockingDeque);
            //向队列里添加数据
            delayedQueue.offer(id.toString(), KafkaConstant.DELAY_TIME, TimeUnit.SECONDS);
            log.info("添加延时队列成功 ，延迟时间：{}，订单id：{}", KafkaConstant.DELAY_TIME, id);
        } catch (Exception e) {
            log.error("添加延时队列失败 ，延迟时间：{}，订单id：{}", KafkaConstant.DELAY_TIME, id);
            e.printStackTrace();
        }
    }

    /**
     * 余额支付成功保存交易数据
     *
     * @param orderNo
     */
    @Override
    public void orderPaySuccess(String orderNo) {
        //修改订单状态，改为支付成功
        LambdaUpdateWrapper<OrderInfo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(OrderInfo::getOrderStatus, SystemConstant.ORDER_STATUS_PAID);
        updateWrapper.eq(OrderInfo::getOrderNo, orderNo);
        this.update(updateWrapper);
        //  发送用户支付成功消息,监听并更新用户支付记录
        // kafkaService.sendMessage(JSON.toJSONString(userPaidRecordVo), KafkaConstant.QUEUE_USER_PAY_RECORD);

    }

    /**
     * 根据订单编号获取订单对象
     *
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        //  获取订单对象
        OrderInfo orderInfo = this.getOne(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getOrderNo, orderNo));
        List<OrderDetail> orderDetailList = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>()
                .eq(OrderDetail::getOrderId, orderInfo.getId()));
        // 赋值订单明细
        orderInfo.setOrderDetailList(orderDetailList);
        //  获取支付方式：
        orderInfo.setPayWayName(this.getPayWayName(orderInfo.getPayWay()));
        // 返回数据
        return orderInfo;
    }

    /**
     * 根据payWay 返回支付名称
     *
     * @param payWay
     * @return
     */
    private String getPayWayName(String payWay) {
        //  声明一个对象
        return SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay) ? "微信支付" : "余额支付";
    }
}
